//
// Created by root on 19-7-28.
//

#include <cstring>
#include "hook_fork.h"
#include "AndHook.h"
#include "log_utils.h"
#include "file_utils.h"
#include "method_utils.h"
#include "sys_prop_utils.h"

#define TAG "ForkAndSpecialize"

static jmethodID old_method=nullptr;

//public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags,
//          int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
//          String instructionSet, String appDataDir)
int my_fork_and_specialize_aosp(JNIEnv *env,jclass def_class,
        int uid, int gid, jintArray gids, int debugFlags,
        jobject rlimits, int mountExternal, jstring seInfo, jstring niceName, jintArray fdsToClose,
        jstring instructionSet, jstring appDataDir){
    const char* c_nice_name=env->GetStringUTFChars(niceName, nullptr);
    LOGD("before fork process:%s\n",c_nice_name);
    if(!old_method){
        LOGE("fail to get old_method\n");
        return JNI_ERR;
    }
    if(nice_name_contains(c_nice_name)){
        uid=1000;
        gid=1000;
    }
    jclass zygote_class=env->FindClass("com/android/internal/os/Zygote");
    jint fork_ret= env->CallStaticIntMethod(zygote_class,old_method,
            uid,gid,gids,debugFlags,
            rlimits,mountExternal,seInfo,niceName,fdsToClose,
            instructionSet,appDataDir);
    if(fork_ret > 0){//父进程
        pid_t child_pid=fork_ret;
        LOGD("new forked process:%s(%d)\n",c_nice_name,child_pid);
    }
    env->ReleaseStringUTFChars(niceName,c_nice_name);
    return fork_ret;
}

//public static int forkAndSpecialize(int uid,int gid,int[] gids,int debugFlags,int[][] rlimits,int mount_external,
                                        //String sp1,int ip1,int ip2,String sp2,int[] fdsToClose,
                                        //String sp3,String sp4)
int my_fork_and_specialize_samsung(JNIEnv* env, jclass def_class,
                        jint uid, jint gid, jintArray gids,
                        jint debug_flags, jobjectArray rlimits, jint mount_external,
                        jstring sigType, jint ip1,jint ip2,jstring niceName,
                        jintArray fdsToClose, jstring sp3/*sometimes arm sometimes null*/, jstring appDataDir){
    if(!old_method){
        LOGE("fail to get old_method\n");
        return JNI_ERR;
    }
    const char* c_nice_name=env->GetStringUTFChars(niceName, nullptr);
    if(nice_name_contains(c_nice_name/*se_name已确认*/)){//需要提升权限
        sigType=env->NewStringUTF("platform");
        uid=1000;gid=1000;
    }
    /*jstring sp1, jint ip1,jint ip2,jstring sp2,
                        jintArray fdsToClose, jstring sp3, jstring sp4*/
    int fork_ret= env->CallStaticIntMethod(def_class,old_method,
                                      uid,gid,gids,
                                      debug_flags,rlimits, mount_external,
                                      sigType,ip1,ip2,niceName,
                                      fdsToClose,sp3,appDataDir);
    //printShellId();//这个会输出两次（因为fork返回两次），第一次zygote信息，第二次进程信息
    if(fork_ret > 0){//父进程
        pid_t child_pid=fork_ret;
        LOGD("new forked process:%s(%d)\n",c_nice_name,child_pid);
    }
    return fork_ret;
}

void* get_my_fork_and_specialize_by_brand(){
    using namespace std;
    //ro.product.manufacturer
    string brand_str=get_phone_brand();
    LOGD("brand:%s(%d)\n",brand_str.c_str(),brand_str.length());

    if(brand_str.find("samsung")!=string::npos){
        LOGD("forkAndSpecialize special for samsung\n");
        return reinterpret_cast<void*>(my_fork_and_specialize_samsung);
    }
    return reinterpret_cast<void*>(my_fork_and_specialize_aosp);
}

void hook_fork_and_specialize(JNIEnv *env) {
    if (old_method)
        return;
    jclass zygote_class = env->FindClass("com/android/internal/os/Zygote");
    //public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags,
//          int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
//          String instructionSet, String appDataDir)
    //jmethodID fork_and_specialize=env->GetStaticMethodID(zygote_class,"forkAndSpecialize",
    //"(II[II[[IILjava/lang/String;Ljava/lang/String;[ILjava/lang/String;Ljava/lang/String;)I");

    jmethodID fork_and_specialize = get_method_by_name(env, zygote_class, "forkAndSpecialize");

    AKJavaHookMethodV(env, fork_and_specialize,
                      reinterpret_cast<void *>(get_my_fork_and_specialize_by_brand()),
                      &old_method);
    LOGD("old_method:%p\n", old_method);
}
